﻿using EOM.TSHotelManagement.Common.Contract;
using EOM.TSHotelManagement.Common.Core;
using EOM.TSHotelManagement.Common.Util;
using EOM.TSHotelManagement.EntityFramework;
using EOM.TSHotelManagement.Shared;
using jvncorelib.EntityLib;
using System.Text;

namespace EOM.TSHotelManagement.Application
{
    public class DashboardService : IDashboardService
    {
        /// <summary>
        /// 房间
        /// </summary>
        private readonly GenericRepository<Room> roomRepository;

        /// <summary>
        /// 房间
        /// </summary>
        private readonly GenericRepository<RoomType> roomTypeRepository;

        /// <summary>
        /// 预约
        /// </summary>
        private readonly GenericRepository<Reser> reserRepository;

        /// <summary>
        /// 客户
        /// </summary>
        private readonly GenericRepository<Customer> customerRepository;

        /// <summary>
        /// 客户类型
        /// </summary>
        private readonly GenericRepository<CustoType> custoTypeRepository;

        /// <summary>
        /// 消费信息
        /// </summary>
        private readonly GenericRepository<Spend> spendRepository;

        /// <summary>
        /// 商品
        /// </summary>
        private readonly GenericRepository<SellThing> sellThingRepository;

        /// <summary>
        /// 员工
        /// </summary>
        private readonly GenericRepository<Employee> employeeRepository;

        /// <summary>
        /// 部门
        /// </summary>
        private readonly GenericRepository<Department> departmentRepository;

        /// <summary>
        /// 考勤打卡
        /// </summary>
        private readonly GenericRepository<EmployeeCheck> employeeCheckRepository;

        /// <summary>
        /// 数据保护
        /// </summary>
        private readonly DataProtectionHelper dataProtector;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="roomRepository"></param>
        /// <param name="roomTypeRepository"></param>
        /// <param name="reserRepository"></param>
        /// <param name="customerRepository"></param>
        /// <param name="custoTypeRepository"></param>
        /// <param name="spendRepository"></param>
        /// <param name="sellThingRepository"></param>
        /// <param name="employeeRepository"></param>
        /// <param name="departmentRepository"></param>
        /// <param name="employeeCheckRepository"></param>
        /// <param name="dataProtector"></param>
        public DashboardService(GenericRepository<Room> roomRepository, GenericRepository<RoomType> roomTypeRepository, GenericRepository<Reser> reserRepository, GenericRepository<Customer> customerRepository, GenericRepository<CustoType> custoTypeRepository, GenericRepository<Spend> spendRepository, GenericRepository<SellThing> sellThingRepository, GenericRepository<Employee> employeeRepository, GenericRepository<Department> departmentRepository, GenericRepository<EmployeeCheck> employeeCheckRepository, DataProtectionHelper dataProtector)
        {
            this.roomRepository = roomRepository;
            this.roomTypeRepository = roomTypeRepository;
            this.reserRepository = reserRepository;
            this.customerRepository = customerRepository;
            this.custoTypeRepository = custoTypeRepository;
            this.spendRepository = spendRepository;
            this.sellThingRepository = sellThingRepository;
            this.employeeRepository = employeeRepository;
            this.departmentRepository = departmentRepository;
            this.employeeCheckRepository = employeeCheckRepository;
            this.dataProtector = dataProtector;
        }

        /// <summary>
        /// 获取房间统计信息
        /// </summary>
        /// <returns></returns>
        public RoomStatisticsOutputDto RoomStatistics()
        {
            RoomStatisticsOutputDto roomStatisticsOutputDto = new RoomStatisticsOutputDto();

            try
            {
                var helper = new EnumHelper();
                var roomStates = Enum.GetValues(typeof(RoomState))
                    .Cast<RoomState>()
                    .Select(e => new EnumDto
                    {
                        Id = (int)e,
                        Name = e.ToString(),
                        Description = helper.GetEnumDescription(e)
                    })
                    .ToList();
                var roomTypes = roomTypeRepository.AsQueryable().Where(a => a.IsDelete != 1).ToList();

                var resers = reserRepository.AsQueryable()
                            .Where(a => a.ReservationEndDate.ToDateTime(TimeOnly.MinValue) >= DateTime.Now.AddDays(-3) && a.IsDelete != 1).ToList();

                var roomStateData = roomRepository.AsQueryable().Where(a => roomStates.Select(b => b.Id).ToList().Contains(a.RoomStateId)).ToList();

                var roomTypeIds = roomTypes.Select(a => a.RoomTypeId).ToList();
                var roomTypeData = roomRepository.AsQueryable().Where(a => roomTypeIds.Contains(a.RoomTypeId) && a.IsDelete != 1).ToList();

                roomStatisticsOutputDto.Status = new TempRoomStatus
                {
                    Vacant = roomStateData.Count(a => a.RoomStateId == (int)RoomState.Vacant),
                    Occupied = roomStateData.Count(a => a.RoomStateId == (int)RoomState.Occupied),
                    Maintenance = roomStateData.Count(a => a.RoomStateId == (int)RoomState.Maintenance),
                    Dirty = roomStateData.Count(a => a.RoomStateId == (int)RoomState.Dirty),
                    Reserved = roomStateData.Count(a => a.RoomStateId == (int)RoomState.Reserved)
                };

                var roomTypeDict = roomTypes.ToDictionary(rt => rt.RoomTypeId, rt => rt.RoomTypeName);
                roomStatisticsOutputDto.Types = roomTypes.GroupBy(a => a.RoomTypeId)
                    .Select(g => new TempRoomType
                    {
                        Name = roomTypeDict.GetValueOrDefault(g.Key, "未知房型"),
                        Total = roomTypeData.Count(a => a.RoomTypeId == g.Key),
                        Remaining = roomTypeData.Count(a => a.RoomTypeId == g.Key && a.RoomStateId == (int)RoomState.Vacant)
                    }).ToList();

                roomStatisticsOutputDto.ReservationAlerts = roomStateData
                       .Where(a => a.RoomStateId == (int)RoomState.Reserved)
                       .Select(a =>
                       {
                           var reservation = resers.SingleOrDefault(b => b.ReservationRoomNumber == a.RoomNumber);
                           var roomType = roomTypes.SingleOrDefault(b => b.RoomTypeId == a.RoomTypeId);
                           return new TempReservationAlert
                           {
                               RoomType = roomType.RoomTypeName,
                               GuestName = reservation?.CustomerName,
                               GuestPhoneNo = reservation != null && !reservation.ReservationPhoneNumber.IsNullOrEmpty()
                               ? dataProtector.DecryptReserData(reservation.ReservationPhoneNumber)
                               : string.Empty,
                               EndDate = reservation?.ReservationEndDate.ToDateTime(TimeOnly.MinValue) ?? DateTime.MinValue
                           };
                       }).ToList();
            }
            catch (Exception ex)
            {
                roomStatisticsOutputDto.Status = new TempRoomStatus
                {
                    Vacant = 0,
                    Occupied = 0,
                    Maintenance = 0,
                    Dirty = 0,
                    Reserved = 0
                };
                roomStatisticsOutputDto.Types = new List<TempRoomType>();
                roomStatisticsOutputDto.ReservationAlerts = new List<TempReservationAlert>();
                roomStatisticsOutputDto.Message = LocalizationHelper.GetLocalizedString(ex.Message, ex.Message);
                roomStatisticsOutputDto.Code = BusinessStatusCode.InternalServerError;
            }

            return roomStatisticsOutputDto;
        }

        /// <summary>
        /// 获取业务统计信息
        /// </summary>
        /// <returns></returns>
        public BusinessStatisticsOutputDto BusinessStatistics()
        {
            var businessStatisticsOutputDto = new BusinessStatisticsOutputDto();

            var nowUtc = DateTime.UtcNow;
            var today = nowUtc.Date;
            var weekStart = today.AddDays(-6);
            var yearStart = today.AddDays(-365);

            try
            {
                var customerTypes = custoTypeRepository.AsQueryable().Where(a => a.IsDelete != 1).ToList();
                var customers = customerRepository.AsQueryable().Where(a => a.IsDelete != 1).ToList();
                var startDate = today.AddYears(-1);
                var allSpends = spendRepository.AsQueryable()
                    .Where(a => a.ConsumptionTime >= startDate && a.IsDelete != 1)
                    .ToList();

                businessStatisticsOutputDto.GenderRatio = new TempGenderRatio
                {
                    Male = customers.Count(a => a.CustomerGender == (int)GenderType.Male),
                    Female = customers.Count(a => a.CustomerGender == (int)GenderType.Female)
                };

                var memberTypeDict = customerTypes.ToDictionary(rt => rt.CustomerType, rt => rt.CustomerTypeName);
                businessStatisticsOutputDto.MemberTypes = customerTypes
                    .GroupBy(a => a.CustomerType)
                    .Select(g => new TempMemberType
                    {
                        Type = customerTypes.Any(ct => ct.CustomerType == g.Key)
                               ? memberTypeDict[g.Key]
                               : "未知类型",
                        Count = customers.Count(a => a.CustomerType == g.Key)
                    })
                    .ToList();

                businessStatisticsOutputDto.DailyConsumption = new TempDailyConsumption
                {
                    Total = allSpends.Where(a => a.ConsumptionTime.Date == today)
                    .Sum(a => a.ConsumptionAmount),
                    Settled = allSpends.Where(a => a.ConsumptionTime.Date == today &&
                                                 a.SettlementStatus == ConsumptionConstant.Settled.Code)
                      .Sum(a => a.ConsumptionAmount)
                };

                businessStatisticsOutputDto.WeeklyConsumption = new TempDailyConsumption
                {
                    Total = allSpends.Where(a => a.ConsumptionTime.Date >= weekStart)
                    .Sum(a => a.ConsumptionAmount),
                    Settled = allSpends.Where(a => a.ConsumptionTime.Date >= weekStart &&
                                                 a.SettlementStatus == ConsumptionConstant.Settled.Code)
                      .Sum(a => a.ConsumptionAmount)
                };

                businessStatisticsOutputDto.YearConsumption = new TempDailyConsumption
                {
                    Total = allSpends.Where(a => a.ConsumptionTime.Date >= yearStart)
                    .Sum(a => a.ConsumptionAmount),
                    Settled = allSpends.Where(a => a.ConsumptionTime.Date >= yearStart &&
                                                 a.SettlementStatus == ConsumptionConstant.Settled.Code)
                      .Sum(a => a.ConsumptionAmount)
                };

                businessStatisticsOutputDto.TotalConsumption = new TempDailyConsumption
                {
                    Total = allSpends.Sum(a => a.ConsumptionAmount),
                    Settled = allSpends.Where(a => a.SettlementStatus == ConsumptionConstant.Settled.Code)
                      .Sum(a => a.ConsumptionAmount)
                };
            }
            catch (Exception ex)
            {
                businessStatisticsOutputDto.Message = LocalizationHelper.GetLocalizedString(ex.Message, ex.Message);
                businessStatisticsOutputDto.Code = BusinessStatusCode.InternalServerError;
            }

            return businessStatisticsOutputDto;
        }

        /// <summary>
        /// 获取后勤统计信息
        /// </summary>
        /// <returns></returns>
        public LogisticsDataOutputDto LogisticsStatistics()
        {
            var dto = new LogisticsDataOutputDto();
            try
            {
                var sellThings = sellThingRepository.AsQueryable()
                    .Where(a => a.IsDelete != 1)
                    .ToList();

                var TotalCount = sellThings.Count;
                if (TotalCount == 0)
                {
                    dto.TotalProducts = 0;
                    dto.InventoryWarning = new TempInventoryWarning
                    {
                        Percent = 0,
                        Status = "success",
                        Text = LocalizationHelper.GetLocalizedString("No products", "暂无商品")
                    };
                    return dto;
                }

                var dangerProducts = sellThings
                    .Where(a => a.Stock <= 50)
                    .Select(a => a.ProductName)
                    .ToList();

                var warningProducts = sellThings
                    .Where(a => a.Stock > 50 && a.Stock <= 100)
                    .Select(a => a.ProductName)
                    .ToList();

                var statusBuilder = new StringBuilder();
                var lowStockList = new List<string>();

                if (dangerProducts.Any())
                {
                    dto.InventoryWarning = new TempInventoryWarning
                    {
                        Status = "error",
                        Percent = (int)Math.Round((dangerProducts.Count * 100.0) / TotalCount),
                        Text = LocalizationHelper.GetLocalizedString(
                            $"{dangerProducts.Count} products in critical stock",
                            $"{dangerProducts.Count}种商品库存告急"),
                        LowStockProducts = dangerProducts
                    };
                }
                else if (warningProducts.Any())
                {
                    dto.InventoryWarning = new TempInventoryWarning
                    {
                        Status = "warning",
                        Percent = (int)Math.Round((warningProducts.Count * 100.0) / TotalCount),
                        Text = LocalizationHelper.GetLocalizedString(
                            $"{warningProducts.Count} products in low stock",
                            $"{warningProducts.Count}种商品库存预警"),
                        LowStockProducts = warningProducts
                    };
                }
                else
                {
                    dto.InventoryWarning = new TempInventoryWarning
                    {
                        Status = "success",
                        Percent = 0,
                        Text = LocalizationHelper.GetLocalizedString("Stock normal", "库存正常"),
                        LowStockProducts = new List<string>()
                    };
                }

                dto.TotalProducts = (int)sellThings.Sum(a => a.Stock);
                dto.RecentRecords = spendRepository.AsQueryable()
                    .Where(a => a.IsDelete != 1)
                    .OrderByDescending(a => a.ConsumptionTime)
                    .Take(3)
                    .Select(a => new TempInventoryRecord
                    {
                        RecordId = a.SpendNumber,
                        OperationType = a.ConsumptionType == SpendType.Product.Code || a.ConsumptionType == SpendType.Other.Code ? TempInventoryOperationType.Outbound
                        : TempInventoryOperationType.Inbound,
                        ProductName = a.ProductName,
                        Quantity = a.ConsumptionQuantity
                    }).ToList();

                dto.Code = BusinessStatusCode.Success;
            }
            catch (Exception)
            {
                dto.Code = BusinessStatusCode.InternalServerError;
                dto.Message = LocalizationHelper.GetLocalizedString(
                    "System error, please try again later",
                    "系统繁忙，请稍后重试"
                );
            }
            return dto;
        }

        /// <summary>
        /// 获取人事统计信息
        /// </summary>
        /// <returns></returns>
        public HumanResourcesOutputDto HumanResourcesStatistics()
        {
            var humanResourcesOutputDto = new HumanResourcesOutputDto();

            try
            {
                var employees = employeeRepository.AsQueryable().Where(a => a.IsDelete != 1).ToList();
                var departments = departmentRepository.AsQueryable().Where(a => a.IsDelete != 1).ToList();
                var employeeIds = employees.Select(a => a.EmployeeId).ToList();
                var employeeChecks = employeeCheckRepository.AsQueryable().Where(a => employeeIds.Contains(a.EmployeeId) && a.IsDelete != 1).ToList();

                humanResourcesOutputDto.TotalEmployees = employees.Count;
                humanResourcesOutputDto.TotalDepartments = departments.Count;
                humanResourcesOutputDto.Attendance = new TempAttendanceRecord
                {
                    Present = employeeChecks.Count(a => a.CheckTime.Hour <= 9 && a.CheckTime.Hour >= 18),
                    Late = employeeChecks.Count(a => a.CheckTime.Hour > 9),
                    Absent = employees.Count - employeeChecks.Count(a => a.CheckTime.Date == DateTime.Now.Date)
                };

            }
            catch (Exception ex)
            {
                humanResourcesOutputDto.Message = LocalizationHelper.GetLocalizedString(ex.Message, ex.Message);
                humanResourcesOutputDto.Code = BusinessStatusCode.InternalServerError;
            }

            return humanResourcesOutputDto;
        }
    }
}
